/*
 * Doctor
 */
package ocp.bundles.doctors.impl;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import ocp.service.ContextConsumer;
import ocp.service.ContextService;
import ocp.context.Attribute;
import ocp.context.Context;
import ocp.context.ContextualInformation;
import ocp.context.ContextualInformationAbsentException;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;

/**
 * Clase que representa un doctor consumidor de contexto
 * @author Paco Lopez Marmol
 */
public class Doctor implements ContextConsumer,Runnable {

    private static final Object SCREEN_MUTEX = new Object();
    
    private String name;
    private ContextService cs;
    private ServiceRegistration sr;
    private Thread thread;
    private boolean running = false;
    

    private List<String> pacientes;
    private boolean pacientesRegistrados = false;
    private int registrados = 0;

    /**
     * Constructor del doctor
     * @param name nombre del doctor
     * @param bc BundleContex 
     */
    public Doctor( String name , BundleContext bc ) {
        this.name = name;
        this.thread = new Thread(this);
        
        pacientes = new ArrayList<String>();
        
        // Registra el servicio ContextConsumer
        Hashtable props = new Hashtable();
        props.put("id",  name ); 
        sr = bc.registerService( ContextConsumer.class.getName(), this , props );
    }
    
    /**
     * Activa el thread
     */
    public void start() {
       running = true; 
       this.thread.start();   
    }
    
    /**
     * Para el thread y quita el registro del servicio 
     */
    public void stop()  {
        running = false;
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        sr.unregister();  // opcional
    }
    
    /**
     * Hilo del thread
     */
    public void run() {
        Random r = new Random();

        try {
            Thread.sleep(10000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        
        while (running) {
            
            // Registra como mucho a 2 pacientes con probabilidad del 2%
            if( cs!=null && registrados<2)  {
                for( String id:pacientes)  {
                    if( r.nextFloat() < 0.02  )  {
                        cs.register(id , this );
                        registrados++;
                    }
                }
            }
                    
            for( String id:pacientes)  {
               if( r.nextFloat() < 0.05  )
                  readPatientContext( id );
            }
           
            try {
                Thread.sleep(4000);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            
        }
        
    }
    
    /**
     * Lee un contexto de ocp y los muestra por la consola
     * @param id el identificador del contexto a mostrar
     */
    private void readPatientContext(String id) { 
        if( cs == null ) return;
        Context context = cs.read(id);
        if( context != null)
           printContext(context,"(read)");
    }

    /**
     * Muestra los datos de un contexto por la consola
     * @param context Contexto para mostrar
     * @param type Fuente del contexto: read o notify
     */
    private void printContext(Context context,String type){
        try {
            synchronized (SCREEN_MUTEX) {
                //System.out.println("===========================================");
                //System.out.println("DOCTOR: Doctor: " + name);
                System.out.println( getFullId() + "----------------- Context " + type + " -----------------");
                System.out.println( getFullId() + "Name: " + context.getName());
                System.out.println( getFullId() + "Id: " + context.getId());
                for (Enumeration<ContextualInformation> e = context.getContextualInformation().getContextualInformation(); e.hasMoreElements();) {
                    ContextualInformation ci = e.nextElement();
                    if (ci != null) {
                        Enumeration<Attribute> ca = ci.getAttributes();
                        while (ca.hasMoreElements()) {
                            Attribute attribute = ca.nextElement();
                            String attributeName = attribute.getAttributeDefinition().getName();
                            System.out.println( getFullId() + " " + attributeName + ": " + attribute.getValue() );
                        }
                    }
                }
                System.out.println( getFullId() + "===========================================");
            }
        } catch (ContextualInformationAbsentException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Identificador para uso en log
     * @return el identificador 
     */
    private String getFullId() {
        return new String( "DOCTOR[" + name +"]: ");
    }
    
    
    /**
     * Activa el ContextConsumer. Mensaje desde ocp para indicarnos que ya
     * podemos hacer uso del servicio ContextService.
     * @param cs Interfaz del servicio ContextService
     * @see ContextService
     */
    public void activate(ContextService cs) {
        this.cs = cs;
    }

    /**
     * Desactiva el ContextConsumer. Mensaje desde ocp para indicarnos que el
     * servicio ocp no esta disponible.
     */
    public void deactivate() {
        cs = null;
    }

    /**
     * En caso de que el ContextConsumer se haya subscrito a los cambios de un 
     * contexto ocp nos notificará enviandonos el contexto actualizado.
     * @param context Contexto que cambia
     */
    public void notifyContextChange(Context context) {
        printContext(context,"(notify)");
    }

    /**
     * ocp nos notifica de los contextos nuevos creados en el sistema
     * @param context Contexto nuevo introducido en ocp
     */
    public void notifyNewContext(Context context) {
        System.out.println( getFullId() + " notify New Context Id: ["  + context.getId() + "] ");
        pacientes.add(  context.getId() );
    }

    /**
     * Obtiene el identificador del ContextConsumer. 
     * @return el identificador
     */
    public String getId() {
        return name;
    }
}
